package com.neucloud.da.edgeside.model.service.impl;

import com.neucloud.da.edgeside.model.exception.BusinessException;
import com.neucloud.da.edgeside.model.service.IStorageService;
import com.neucloud.da.edgeside.model.utils.EncodingDetector;
import io.minio.*;
import io.minio.http.Method;
import io.minio.messages.Item;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;

@Slf4j
public abstract class StorageServiceImpl implements IStorageService {

    public MinioClient minioClient;

    @SneakyThrows
    @Override
    public Boolean bucketExist(String bucketName){
        return minioClient.bucketExists(BucketExistsArgs.builder()
                .bucket(bucketName)
                .build());
    }

    @SneakyThrows
    @Override
    public void createBucket(String bucketName) {
        // 如果该bucket不存在则创建
        if (!bucketExist(bucketName)) {
            minioClient.makeBucket(MakeBucketArgs.builder()
                    .bucket(bucketName)
                    .build());
        }
    }

    @SneakyThrows
    @Override
    public void deleteBucket(String bucketName) {
        if (!bucketExist(bucketName)) {
            return;
        }
        Iterable<Result<Item>> results = minioClient.listObjects(ListObjectsArgs.builder()
                .bucket(bucketName)
                .recursive(true)
                .build());
        for (Result<Item> result : results) {
            deleteObject(bucketName,result.get().objectName());
        }
        minioClient.removeBucket(RemoveBucketArgs.builder()
                .bucket(bucketName)
                .build());
    }

    @Override
    public void uploadFile(String bucketName, String objectName, MultipartFile file) {
        // 判断bucket是否存在
        if(!bucketExist(bucketName)){
            createBucket(bucketName);
        }
        try {
            minioClient.putObject(PutObjectArgs.builder()
                    .bucket(bucketName)
                    .object(objectName)
                    .stream(file.getInputStream(),file.getSize(),-1)
                    .contentType(file.getContentType())
                    .build());
        } catch (Exception e) {
            log.error("upload exception:{}", e.getMessage());
            throw new BusinessException("上传文件异常");
        }
    }

    @Override
    public void uploadFile(String bucketName, String objectName, InputStream fileStream,Long fileSize) {
        try {
            minioClient.putObject(PutObjectArgs.builder()
                    .bucket(bucketName)
                    .object(objectName)
                    .stream(fileStream,fileSize,-1)
                    .build());
        } catch (Exception e) {
            log.error("upload exception:{}", e.getMessage());
            throw new BusinessException("上传文件异常");
        }
    }


    @SneakyThrows
    @Override
    public String generateUrl(String bucketName, String objectName){
        // 判断bucket是否存在
        if (!bucketExist(bucketName)) {
            log.error("bucket is not exist:{}", bucketName);
            throw new BusinessException("bucket不存在");
        }
        return minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
                .bucket(bucketName)
                .object(objectName)
                .method(Method.GET)
                .build());
    }

    @SneakyThrows
    @Override
    public void deleteObject(String bucketName, String objectName){
        // 判断bucket是否存在
        if (!bucketExist(bucketName)) {
            throw new BusinessException("存储空间不存在");
        }
        minioClient.removeObject(RemoveObjectArgs.builder()
                .bucket(bucketName)
                .object(objectName)
                .build());
    }

    @Override
    public String getObjectContent(String bucketName, String objectName){
        String content = null;
        InputStream obj = null;
        try{
            obj = getObject(bucketName,objectName);
            String encoding = EncodingDetector.detectEncoding(obj);
            content= IOUtils.toString(getObject(bucketName,objectName), encoding);
        }catch (Exception ex){
            log.error("get s3 object content error:{}", ex.getMessage());
        }finally {
            try {
                obj.close();
            } catch (IOException e) {
                log.error("get object from s3, close InputStream error:{}", e.getMessage());
            }
        }
        return content;
    }

    @SneakyThrows
    @Override
    public void objectExist(String bucketName,String objectName) {
        minioClient.statObject(StatObjectArgs.builder()
                .bucket(bucketName)
                .object(objectName)
                .build());
    }

    @SneakyThrows
    @Override
    public InputStream getObject(String bucketName, String objectName) {
        if (!bucketExist(bucketName)) {
            log.error("bucket is not exist:{}", bucketName);
            throw new BusinessException("bucket不存在");
        }
        return minioClient.getObject(GetObjectArgs.builder()
                .bucket(bucketName)
                .object(objectName)
                .build());
    }

    @SneakyThrows
    @Override
    public boolean copyObject(String srcBucketName, String srcObjectName, String targetBucketName, String targetObjectName,boolean isDirect) {
        if (!bucketExist(srcBucketName)) {
            log.warn("srcBucketName:{} not found bucketName",srcBucketName);
            return false;
        }
        if (!bucketExist(targetBucketName)) {
            log.warn("targetBucketName:{} not found bucketName",targetBucketName);
            return false;
        }
        if(!isDirect){
            minioClient.copyObject(CopyObjectArgs.builder()
                    .source(CopySource.builder()
                            .bucket(srcBucketName)
                            .object(srcObjectName)
                            .build())
                    .bucket(targetBucketName)
                    .object(targetObjectName)
                    .build());
        }else {
            Iterable<Result<Item>> results = minioClient.listObjects(ListObjectsArgs.builder()
                    .keyMarker(srcObjectName)
                    .bucket(srcBucketName)
                    .recursive(true)
                    .build());
            String srcObj;
            String targetObj;
            for (Result<Item> result : results) {
                srcObj = result.get().objectName();
                targetObj = srcObj.replace(srcObjectName, targetObjectName);
                if(srcObj.equals(targetObj)){
                    continue;
                }
                minioClient.copyObject(CopyObjectArgs.builder()
                        .source(CopySource.builder()
                                .bucket(srcBucketName)
                                .object(srcObj)
                                .build())
                        .bucket(targetBucketName)
                        .object(targetObj)
                        .build());
            }
        }
        return true;
    }

    @SneakyThrows
    @Override
    public boolean downObject(String bucketName, String objectName, String localPath) {
        log.info("bucketName:{} objectName:{} localPath:{} down start",bucketName,objectName,localPath);
        if (!bucketExist(bucketName)) {
            log.warn("down bucketName:{} objectName:{} localPath:{} not found bucketName",bucketName,objectName,localPath);
            return false;
        }
        Iterable<Result<Item>> results = minioClient.listObjects(ListObjectsArgs.builder()
                .bucket(bucketName)
                .prefix(objectName)
                .recursive(true)
                .build());
        int fileSize = 0;
        for (Result<Item> result : results) {
            Item item = result.get();
            String fileName = (localPath.endsWith(File.separator) ? localPath : localPath + File.separator) + item.objectName().split(objectName)[1];
            File file=new File(fileName);
            //路径的父级及以上的目录实例化的File对象是否存在
            if(!file.getParentFile().exists()){
                file.getParentFile().mkdirs();
            }
            minioClient.downloadObject(DownloadObjectArgs.builder()
                    .bucket(bucketName)
                    .object(item.objectName())
                    .filename(fileName)
                    .overwrite(true)
                    .build());
            log.info("bucketName:{} objectName:{} itemName:{} itemSize:{} down complete",bucketName,objectName,item.objectName(),item.size());
            fileSize++;
        }
        log.info("bucketName:{} objectName:{} localPath:{} fileSize:{} down complete",bucketName,objectName,localPath,fileSize);
        return true;
    }

    @SneakyThrows
    @Override
    public long calculate(String bucketName, String objectName) {
        if (!bucketExist(bucketName)) {
            return 0;
        }
        Iterable<Result<Item>> results = minioClient.listObjects(ListObjectsArgs.builder()
                .bucket(bucketName)
                .prefix(objectName)
                .recursive(true)
                .build());
        long size = 0;
        for (Result<Item> result : results) {
            Item item = result.get();
            size += item.size();
        }
        return size;
    }
}
